#ifndef _ICS_TYPES_H
#define _ICS_TYPES_H

#include <string>
#include <list>
#include <cstdio>
#include <ctime>
#include <iostream>

using namespace std;

namespace icslib {


template< class enumeration >
constexpr std::size_t enum_index( const enumeration value ) noexcept
{
     static_assert( std::is_enum< enumeration >::value, "Not an enum" );
     return static_cast< std::size_t >( value );
}

/**
 * @brief The TimeUnit enum
 */
enum class TimeUnit
{
    kYear = 0,
    kMonth,
    kDay,
    kHour,
    kMinute,
    kSecond,
    kWeek
};


/**
 * @brief The Date class
 */
class Date {

private:
    /**
     * @brief compare
     * @param otherDate
     * @param fromPart
     * @return
     */
    int compare(const Date &otherDate, TimeUnit fromPart = TimeUnit::kYear) const {
        for ( ;fromPart < TimeUnit::kSecond; fromPart = (TimeUnit)(enum_index(fromPart) + 1)) {
            if(m_nArrData[enum_index(fromPart)] < otherDate.m_nArrData[enum_index(fromPart)]) {
                return -1;
            } else if(m_nArrData[enum_index(fromPart)] > otherDate.m_nArrData[enum_index(fromPart)]) {
                return 1;
            }
        }
        return 0;
    }

    class DatePart;

public:
    Date() {

    }
    /**
     * @brief isLeapYear 判断闰年
     * @param year
     * @return
     */
    bool isLeapYear(short year) const {
        return ((year%4 == 0 && year%100 != 0) || year%400 == 0);
    }
    /**
     * @brief daysInMonth 一个月的天数
     * @param month
     * @return
     */
    short daysInMonth(short month = 0) const {
        switch (month) {
            case 1: return 31;
            case 2: return isLeapYear(m_nArrData[enum_index(TimeUnit::kYear)]) ? 29:28;
            case 3: return 31;
            case 4: return 30;
            case 5: return 31;
            case 6: return 30;
            case 7: return 31;
            case 8: return 31;
            case 9: return 30;
            case 10: return 31;
            case 11: return 30;
            case 12: return 31;
        }
        return 0;
    }

    std::string format() const;
    operator std::string() const;
    Date &operator =(const std::string &text);

    friend ostream &operator <<(ostream &stream, const Date &dt) {
            stream << dt.operator std::string();
            return stream;
     }

    bool operator <=(const Date &otherDate) const { return (compare(otherDate) <= 0); }
    bool operator >=(const Date &otherDate) const { return (compare(otherDate) >= 0); }
    bool operator <(const Date &otherDate) const { return (compare(otherDate) < 0); }
    bool operator >(const Date &otherDate) const { return (compare(otherDate) >0);}
    bool operator ==(const Date &otherDate) const {return (compare(otherDate) == 0);}

    DatePart operator [](TimeUnit partName);

    unsigned long difference(Date &otherDate, TimeUnit unit, bool roundUp = true) const;

    void setToNow();
    bool isEmpty() const {return (m_nArrData[enum_index(TimeUnit::kYear)] == 0) ;}

private:
    /**
     * @brief data
     */
    short m_nArrData[6] = {'\0'};

    bool m_bWithTime = true;
};

class Date::DatePart {

public:
    DatePart(Date &base, TimeUnit part): m_baseDate(base), m_part(part) {}

private:
    Date &m_baseDate;
    TimeUnit m_part;
};


struct DeleteItem {
    template <typename T>
    void operator()(T *ptr) { delete ptr; }
};

// Custom data types

typedef enum { VCALENDAR, VEVENT, VALARM } Component; /* 组件 */
typedef enum { DISPLAY=0, PROCEDURE, AUDIO, EMAIL } AlarmAction;

struct Recurrence {   // 重复
    Recurrence(): Freq(TimeUnit::kMinute), Interval(0), Count(0) {}
    operator string() const;
    bool IsEmpty() const { return (Interval == 0); }
    void Clear() { Interval = 0; }

    TimeUnit Freq;
    unsigned short Interval /*间隔*/, Count;
    Date Until;
};

/* 触发报警 */
struct AlarmTrigger {
    AlarmTrigger(): Before(true), Value(0), Unit(TimeUnit::kMinute) {}
    AlarmTrigger &operator =(const string &Text);
    operator string() const;

    bool Before;
    unsigned short Value;
    TimeUnit Unit;
};

struct Alarm {
    Alarm(): Active(false), Action(DISPLAY) {}
    operator string() const;
    void Clear() {
        Description.clear();
    }

    bool Active;
    AlarmAction Action;
    AlarmTrigger Trigger;
    string Description;
};

struct Event {
    Event(): Alarms(new list<Alarm>), RecurrenceNo(0), BaseEvent(this) {}
    Event(const Event &Base):
        UID(Base.UID),
        Summary(Base.Summary),
        Description(Base.Description),
        Categories(Base.Categories),
        DtStamp(Base.DtStamp),
        DtStart(Base.DtStart),
        DtEnd(Base.DtEnd),
        RRule(Base.RRule),
        Alarms(Base.Alarms),
        RecurrenceNo(Base.RecurrenceNo)
    {
        BaseEvent = Base.BaseEvent == (Event *)&Base ? (Event *)&Base : Base.BaseEvent;
    }
    ~Event() {
        if (BaseEvent == this)
            delete Alarms;
    }
    operator string() const;
    bool HasAlarm(const Date &From, const Date &To);

    string UID, Summary, Description, Categories;
    Date DtStamp, DtStart, DtEnd;
    Recurrence RRule;
    list<Alarm> *Alarms;
    unsigned short RecurrenceNo;
    Event *BaseEvent;
};

struct EventsCriteria {
    EventsCriteria(): AllEvents(false), IncludeRecurrent(true) {}

    Date From, To;
    bool AllEvents, IncludeRecurrent;
};

inline ostream &operator <<(ostream &stream, const Recurrence &RRule) {
    stream << RRule.operator string();
    return stream;
}

inline ostream &operator <<(ostream &stream, const Alarm &Alarm) {
    stream << Alarm.operator string();
    return stream;
}

inline ostream &operator <<(ostream &stream, const Event &Event) {
    stream << Event.operator string();
    return stream;
}


}
#endif
